package saxion.sensorframework;

import java.io.File;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;


import saxion.sensorframework.db.dao.DaoSensor;
import saxion.sensorframework.structuur.DataStruc;
import saxion.sensorframework.structuur.Sensor;
import saxion.sensorframework.structuur.Sensors;

/**
 * Classe for processing client request/task
 * @author Erik & Martijn
 *
 */
public class ClientProcessor {

	static Logger logger = Logger.getLogger(ClientProcessor.class.getName());  /**< logger  \sa SensorFramework::logger */
	private Properties properties;   /**< Properties file. \sa SensorFramework::properties*/
	private DaoSensor daoSensor;   /**< DaoSensor used for database connection */
	private String sensorDataXmlLocation;  /**< Location for xml file's containing sensor data */
	
	private int sensorDataId = 0; /**< id for sensor data xml documents */
	private int sensorsId = 0; /**< id for sensors xml documents */
	private int sensorsFieldId = 0; /**< id for sensors data fields xml documents */
	
	/**
	 * 
	 * @param properties
	 */
	public ClientProcessor(Properties properties)
	{
		this.properties = properties;
		initialize();
	}
	
	/**
	 *  \brief initialize variables
	 *  
	 *  Create dao sensor
	 */
	private void initialize()
	{
		sensorDataXmlLocation = properties.getProperty("sensorDataXmlLocation");
		
		try {
			daoSensor = new DaoSensor();
		} catch (Exception e) {
			logger.error("Unable to create DaoSensor");
			e.printStackTrace();
		}
	}
	
	/**
	 *\brief Get sensors from database
	 *
	 *get sensors from database and write informatie to xml document.
	 *
	 * @return Sensor in xml document
	 * @throws JAXBException 
	 */
	public File getSensors() 
	{
		File file = null;
		ArrayList<Sensor> sensorList = new ArrayList<Sensor> ();
		
		ResultSet resultset = null;
		try {
			resultset = daoSensor.getAllSensors();
		} catch (SQLException e) {
			logger.error("Error getting sensors!");
			e.printStackTrace();
		}
		
		try {
		while(resultset.next())
		{
			Sensor sensor = new Sensor();
			sensor.setId(resultset.getInt("id"));
			sensor.setName(resultset.getString("name"));
			sensor.setDescription(resultset.getString("description"));		
		
			sensorList.add(sensor);
		}
		
		} catch (SQLException e) {
			logger.error("Error creating sensor objects!");
			e.printStackTrace();
		}
		
		Sensors sensors = new Sensors();
		sensors.setSensors(sensorList);

		
		JAXBContext context;
		
		try {
			context = JAXBContext.newInstance(Sensors.class);
			Marshaller m = context.createMarshaller();
			m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			
			file = new File("Sensors" + sensorsId+".xml");
			sensorsId++;
			
			m.marshal(sensors, file);
			
		} catch (JAXBException e) {
			logger.error("Error coverting sensors to xml file!");
			e.printStackTrace();
		}
  
		return file;
	}
	
	/**
	 * \brief Get sensor data fields from database
	 * 
	 * Get sensor data field from the database and write information to xml document.
	 * 
	 * @param sensorId
	 * @return xml document location
	 */
	public File getSensorDataFields(int sensorId)
	{
		File file = null;
		ResultSet resultset = null;
	
		try {
			resultset = daoSensor.getSensorDataTypes(sensorId);
		} catch (SQLException e) {
			logger.error("Error getting sensordata!");
			e.printStackTrace();
		}

		ArrayList<DataStruc> struc = null;
		
		  try{
			  struc = new ArrayList<DataStruc>();
			  
		     while (resultset.next()) {
		    	 DataStruc dataStruc = new  DataStruc(); 
		    	 dataStruc.setName(resultset.getString("Field"));
		    	 dataStruc.setType(resultset.getString("Type"));
		    	 struc.add(dataStruc);
		     }

		 } catch (Exception e) {
			logger.error("Error converting sensor");
			e.printStackTrace();
		 }
	

		Sensor sensor = new Sensor();
		sensor.setId(sensorId);
		sensor.setDatastructuur(struc);
	 	
			JAXBContext context;
			
			try {
				context = JAXBContext.newInstance(Sensor.class);
				Marshaller m = context.createMarshaller();
				m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
				
				file = new File("Sensors" + sensorsFieldId+".xml");
				sensorsFieldId++;
				
				m.marshal(sensor, file);
				
			} catch (JAXBException e) {
				logger.error("Error coverting sensors to xml file!");
				e.printStackTrace();
			}
			
			return file;
	     
	}
	
	/**
	 * \brief Get sensor data 
	 * 
	 * get all the sensor information form database and write to xml document.
	 * 
	 * @param sensorId
	 * @return xml document
	 */
	public File getSensorData(int sensorId)
	{
		ResultSet resultset = null;
		
		try {
			resultset = daoSensor.getSensorData(sensorId);
			
		} catch (Exception e) {
			logger.error("Error getting sensor data!");
			e.printStackTrace();
			return null;
		}

		return createSensorDataXml(sensorId, resultset);

	}
	
	/**
	 * \brief Get sensor data
	 * 
	 * get sensor data limited by minium and maxium value. writes data to xml document.
	 * 
	 * @param sensorId
	 * @param minimum
	 * @param maximum
	 * @return xml document
	 */
	public File getSensorData(int sensorId, long minimum, long maximum)
	{

		ResultSet resultset = null;
		
		try {
			resultset = daoSensor.getSpecificSensorData(minimum, maximum, sensorId);
			
		} catch (Exception e) {
			logger.error("Error getting sensor data!");
			e.printStackTrace();
			return null;
		}
		return createSensorDataXml(sensorId, resultset );
	}
	
	/**
	 * 
	 * \brief create xml document with sensor data
	 * 
	 * Creates custom xml document containing the sensor data.
	 * 
	 * @param sensorId
	 * @param resultset
	 * @return xml document
	 */
	private File createSensorDataXml(int sensorId, ResultSet resultset)
	{
	
		ResultSetMetaData rsmd = null;
		File file = null;

		try {
			rsmd = resultset.getMetaData();
	
		} catch (Exception e) {
			logger.error("Error getting sensor meta data!");
			e.printStackTrace();
			return null;
		}
		
		String[][] fields = null;
		try{
			fields = new String[rsmd.getColumnCount()][2];
			
			for(int i = 1; i < rsmd.getColumnCount(); i++)
			{
				fields[i-1][0] = rsmd.getColumnName(i);
				fields[i-1][1] = rsmd.getColumnTypeName(i);
			}
		
		} catch (Exception e) {
			logger.error("Error getting senors fields!");
			e.printStackTrace();
			return null;
		}
		
		try{
		
			//create xml document
			DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
			
			// root elements
			Document doc = docBuilder.newDocument();
			Element rootElement = doc.createElement("sensordata");
			doc.appendChild(rootElement);
			
			// id elements
			Element id = doc.createElement("id");
			id.appendChild(doc.createTextNode(Integer.toString(sensorId)));
			rootElement.appendChild(id);
			
			Element data = doc.createElement("data");
			rootElement.appendChild(data);
			
			
				while(resultset.next())
				{
					Element row = doc.createElement("row");
					
					
					for(int i =0; i < fields.length -1; i++)
					{
						Element field = doc.createElement(fields[i][0]);
						field.setAttribute("type", fields[i][1]);
						field.appendChild(doc.createTextNode(resultset.getString(fields[i][0])));
						row.appendChild(field);
					}
					data.appendChild(row);
				}
			
			TransformerFactory transformerFactory = TransformerFactory.newInstance();
			Transformer transformer = transformerFactory.newTransformer();
			DOMSource source = new DOMSource(doc);
			
			file = new File(sensorDataXmlLocation +"sensorData"+ sensorDataId+".xml");
			
			StreamResult result = new StreamResult(file);
			transformer.transform(source, result);
			
			sensorDataId++;
			
		} catch (Exception e) {
			logger.error("Error creating xml file!");
			e.printStackTrace();
			return null;
		}

		System.out.println("File saved!");
		
		return file;
	}

}
